import pandas as pd
import logging
from soccer_data_loader import Soccer1X2DataLoader, generate_test_data
from abc import ABC, abstractmethod

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class Soccer1X2Strategy:
    """胜平负策略抽象基类"""
    @abstractmethod
    def generate_signal(self, match_data):
        """
        生成胜平负投注信号
        :param match_data: 单场比赛数据（含赛前赔率、球队信息等）
        :return: dict，格式如{"selection": "1"/"X"/"2", "stake": 100.0}
        """
        pass

class OddsRangeStrategy(Soccer1X2Strategy):
    """
    基于赔率范围的投注策略：
    - 在不同赔率范围内，选择赔率最小的选项进行投注
    - 每次固定投注100元
    """
    def generate_signal(self, match_data):
        # 提取三项赔率
        odds = {
            '1': match_data['home_odds'],
            'X': match_data['draw_odds'],
            '2': match_data['away_odds']
        }
        
        # 找到赔率最小的选项
        min_odds_selection = min(odds, key=odds.get)
        min_odds_value = odds[min_odds_selection]
        
        # 记录选择依据（用于日志和分析）
        selection_reason = f"最小赔率 {min_odds_value:.2f}，选项 {min_odds_selection}"
        
        # 返回投注信号，固定投注100元
        return {
            "selection": min_odds_selection,
            "stake": 100.0,
            "reason": selection_reason
        }

class Soccer1X2BacktestEngine:
    """胜平负玩法回测引擎"""
    def __init__(self, strategy: Soccer1X2Strategy, initial_capital=10000.0):
        self.strategy = strategy
        self.initial_capital = initial_capital
        self.current_capital = initial_capital
        self.trade_history = []  # 记录所有交易
        self.bet_count = 0       # 总投注次数
        self.win_count = 0       # 赢单次数
        
    def run(self, matches: pd.DataFrame):
        """运行回测"""
        if matches is None or len(matches) == 0:
            logger.error("没有有效的比赛数据用于回测")
            return
        
        logger.info(f"开始回测，初始资金: {self.initial_capital}元，共{len(matches)}场比赛")
        
        for idx, match in matches.iterrows():
            try:
                # 生成投注信号
                signal = self.strategy.generate_signal(match)
                
                if not signal or "selection" not in signal or "stake" not in signal:
                    logger.warning(f"第{idx+1}场比赛未生成有效投注信号")
                    continue
                
                # 检查资金是否充足
                if signal["stake"] > self.current_capital:
                    logger.warning(f"第{idx+1}场比赛资金不足，无法投注")
                    continue
                
                # 记录投注信息
                self.bet_count += 1
                bet_info = {
                    "match_id": match['match_id'],
                    "timestamp": match['timestamp'],
                    "home_team": match['home_team'],
                    "away_team": match['away_team'],
                    "selection": signal["selection"],
                    "stake": signal["stake"],
                    "odds": match[f"home_odds" if signal["selection"] == "1" 
                                 else "draw_odds" if signal["selection"] == "X" 
                                 else "away_odds"],
                    "result": match['result'],
                    "capital_before": self.current_capital,
                    "reason": signal.get("reason", "")
                }
                
                # 计算盈亏
                if signal["selection"] == match['result']:
                    # 赢单
                    profit = signal["stake"] * (bet_info["odds"] - 1)
                    self.current_capital += profit
                    self.win_count += 1
                    bet_info["profit"] = profit
                    bet_info["outcome"] = "win"
                else:
                    # 输单
                    profit = -signal["stake"]
                    self.current_capital += profit
                    bet_info["profit"] = profit
                    bet_info["outcome"] = "lose"
                
                bet_info["capital_after"] = self.current_capital
                self.trade_history.append(bet_info)
                
                # 打印单场结果
                logger.info(
                    f"比赛 {match['match_id']}: {match['home_team']} vs {match['away_team']} "
                    f"| 选择: {signal['selection']} | 赔率: {bet_info['odds']:.2f} "
                    f"| 结果: {bet_info['outcome']} | 盈亏: {profit:.2f} | 资金: {self.current_capital:.2f}"
                )
                
            except Exception as e:
                logger.error(f"处理第{idx+1}场比赛时出错: {str(e)}")
                continue
        
        # 回测结束，计算统计信息
        self._calculate_stats()
    
    def _calculate_stats(self):
        """计算回测统计信息"""
        if self.bet_count == 0:
            logger.info("未进行任何投注")
            return
        
        total_profit = self.current_capital - self.initial_capital
        win_rate = (self.win_count / self.bet_count) * 100
        roi = (total_profit / self.initial_capital) * 100
        
        logger.info("\n===== 回测结果统计 =====")
        logger.info(f"初始资金: {self.initial_capital:.2f}元")
        logger.info(f"最终资金: {self.current_capital:.2f}元")
        logger.info(f"总盈亏: {total_profit:.2f}元")
        logger.info(f"总投注次数: {self.bet_count}次")
        logger.info(f"赢单次数: {self.win_count}次")
        logger.info(f"胜率: {win_rate:.2f}%")
        logger.info(f"收益率(ROI): {roi:.2f}%")
        
        # 按赔率范围统计
        self._analyze_by_odds_range()
    
    def _analyze_by_odds_range(self):
        """按赔率范围分析结果"""
        if not self.trade_history:
            return
            
        # 将交易历史转换为DataFrame以便分析
        df = pd.DataFrame(self.trade_history)
        
        # 定义赔率范围
        ranges = [
            ("<2.0", df['odds'] < 2.0),
            ("2.0-3.0", (df['odds'] >= 2.0) & (df['odds'] <= 3.0)),
            (">3.0", df['odds'] > 3.0)
        ]
        
        print("\n===== 按赔率范围分析 =====")
        for range_name, mask in ranges:
            range_data = df[mask]
            if len(range_data) == 0:
                continue
                
            win_rate = (len(range_data[range_data['outcome'] == 'win']) / len(range_data)) * 100
            total_profit = range_data['profit'].sum()
            
            print(f"赔率范围 {range_name}:")
            print(f"  投注次数: {len(range_data)}")
            print(f"  赢单次数: {len(range_data[range_data['outcome'] == 'win'])}")
            print(f"  胜率: {win_rate:.2f}%")
            print(f"  总盈亏: {total_profit:.2f}元")

# 执行回测
def run_strategy_backtest():
    # 生成测试数据
    test_file = generate_test_data()
    
    # 加载数据
    data_loader = Soccer1X2DataLoader()
    matches = data_loader.load_csv(test_file)
    
    if matches is None:
        logger.error("无法加载测试数据，回测终止")
        return
    
    # 创建策略实例
    strategy = OddsRangeStrategy()
    
    # 创建回测引擎并运行回测
    backtest_engine = Soccer1X2BacktestEngine(strategy, initial_capital=10000.0)
    backtest_engine.run(matches)
    
    return backtest_engine

if __name__ == "__main__":
    from abc import ABC, abstractmethod  # 导入抽象基类模块
    run_strategy_backtest()
